#include <stdio.h>
#include <windows.h>

#include "Master.h"
#include "Message.h"
#include "Types.h"

Master* master_;
HWND window_handle_;
static const int DEBUG_STRING_SIZE = 512;
char debug_string[DEBUG_STRING_SIZE] = {0};

static const unsigned short USAGE_PAGE_STD_INPUT = 0x01;
static const unsigned short USAGE_MOUSE = 0x02;
static const unsigned short USAGE_KEYBOARD = 0x06;

void PrintLastError()
{
	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, debug_string, DEBUG_STRING_SIZE, NULL);
	OutputDebugStringA("Last Windows error: ");
	OutputDebugStringA(debug_string);
	OutputDebugStringA("\n");
}

void RawInputToMouseEvent(RAWINPUT* raw_input)
{
	abyte event_type = Message::kEventNone;
	abyte move_type = 0;
	RAWMOUSE* raw_mouse = &(raw_input->data.mouse);
	char* debug_string_pointer = debug_string;
	//debug_string_pointer += sprintf(debug_string_pointer, "Mouse event: ");

	switch(raw_mouse->usButtonFlags)
	{
		case RI_MOUSE_LEFT_BUTTON_DOWN:

			event_type = Message::kEventMouseDown;
			debug_string_pointer += sprintf(debug_string_pointer, "MouseDown");
			break;

		case RI_MOUSE_LEFT_BUTTON_UP:

			event_type = Message::kEventMouseUp;
			debug_string_pointer += sprintf(debug_string_pointer, "MouseUp");
			break;

		case RI_MOUSE_RIGHT_BUTTON_DOWN:

			event_type = Message::kEventRightMouseDown;
			debug_string_pointer += sprintf(debug_string_pointer, "RightMouseDown");
			break;

		case RI_MOUSE_RIGHT_BUTTON_UP:

			event_type = Message::kEventRightMouseUp;
			debug_string_pointer += sprintf(debug_string_pointer, "RightMouseUp");
			break;

		case RI_MOUSE_WHEEL:

			event_type = Message::kEventMouseWheel;
			debug_string_pointer += sprintf(debug_string_pointer, "MouseWheel: %sd", raw_mouse->usButtonData);
			break;

		default:

			if(raw_mouse->lLastX != 0 || raw_mouse->lLastY != 0)
			{
				debug_string_pointer += sprintf(debug_string_pointer, "MouseMove: ");
				debug_string_pointer += sprintf(debug_string_pointer, "%ld, %ld", raw_mouse->lLastX, raw_mouse->lLastY);
				event_type = Message::kEventMouseMove;

				if(raw_mouse->usFlags & MOUSE_MOVE_ABSOLUTE == MOUSE_MOVE_ABSOLUTE)
				{
					move_type = Message::kCoordinateAbsolute;
					debug_string_pointer += sprintf(debug_string_pointer, " (Absolute)");
				}
				else // this means the mouse movement is relative
				{
					move_type = Message::kCoordinateRelative;
					debug_string_pointer += sprintf(debug_string_pointer, " (Other:%hu)", raw_mouse->usFlags);
				}
			}
			break;
	}

	if(event_type == Message::kEventNone)
	{
		OutputDebugStringA("Failed to recognize event\n");
		return;
	}

	OutputDebugStringA(debug_string);
	OutputDebugStringA("\n");
	master_->MouseEventOccured(event_type, move_type, GetTickCount(), raw_mouse->lLastX, raw_mouse->lLastY, raw_mouse->usButtonData);
}

//--------------------------------------------------------------------------------
//Window procedure callback
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
	case WM_INPUT:

		RAWINPUT* raw_input;
		unsigned int size;
		GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
		raw_input = (RAWINPUT*)malloc(size);
		GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_input, &size, sizeof(RAWINPUTHEADER));

		if(raw_input->header.dwType == RIM_TYPEKEYBOARD)
		{
			OutputDebugStringA("Keyboard event\n");
		}
		else if(raw_input->header.dwType == RIM_TYPEMOUSE)
		{
			RawInputToMouseEvent(raw_input);
			//OutputDebugStringA("Mouse event\n");
		}

		free(raw_input);
		break;

	case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

//--------------------------------------------------------------------------------
//Entry point
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    //--------------------------------------------------------------------------------
    //Register a window class
    WNDCLASSEX wcex={0};
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInstance;
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW);   
    wcex.lpszClassName  = L"MinWinApp";
    if(!RegisterClassEx(&wcex))
        return -1;

    RECT rc = { 0, 0, 640, 480 };
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
    HWND hWnd = window_handle_ = CreateWindow(L"MinWinApp", L"Minimum Windows App", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);

    if(!hWnd)
        return -2;


	RAWINPUTDEVICE mouse;
	mouse.usUsagePage = USAGE_PAGE_STD_INPUT;
	mouse.usUsage = USAGE_MOUSE;
	mouse.dwFlags = RIDEV_INPUTSINK;
	mouse.hwndTarget = window_handle_;
	if(!RegisterRawInputDevices(&mouse, 1, sizeof(RAWINPUTDEVICE)))
	{
		PrintLastError();
		MessageBox(window_handle_, L"Failed to register for mouse raw input", L"", 0);
	}

	master_ = new Master("192.168.1.101", 8989);
	master_->Start();
	master_->MouseEventOccured(Message::kEventMouseMove, 1, 1, 1, 1);
    ShowWindow(hWnd, nCmdShow);
    MSG msg = {0};

    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

	delete master_;

	OutputDebugStringA("Exiting WinMain\n");
    return 0;

}